<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SocBackend API 文档</title>
    <style>
        body {
            font-family: 'Courier New', monospace;
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
            background-color: #f5f5f5;
            line-height: 1.6;
        }
        .container {
            background: white;
            padding: 30px;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        h1 {
            color: #333;
            border-bottom: 2px solid #007bff;
            padding-bottom: 10px;
        }
        h2 {
            color: #007bff;
            margin-top: 30px;
            border-left: 4px solid #007bff;
            padding-left: 15px;
        }
        h3 {
            color: #555;
            margin-top: 25px;
        }
        .endpoint {
            background: #f8f9fa;
            border: 1px solid #dee2e6;
            border-radius: 5px;
            padding: 15px;
            margin: 15px 0;
        }
        .method {
            display: inline-block;
            background: #007bff;
            color: white;
            padding: 5px 10px;
            border-radius: 3px;
            font-weight: bold;
            margin-right: 10px;
        }
        .url {
            font-family: monospace;
            background: #e9ecef;
            padding: 5px 10px;
            border-radius: 3px;
            color: #495057;
        }
        .code-block {
            background: #2d3748;
            border: 1px solid #4a5568;
            border-radius: 8px;
            padding: 20px;
            margin: 15px 0;
            overflow-x: auto;
            color: #e2e8f0;
            font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
            font-size: 14px;
            line-height: 1.5;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
        }
        
        .code-block .comment {
            color: #68d391;
        }
        
        .code-block .keyword {
            color: #f6ad55;
        }
        
        .code-block .string {
            color: #9f7aea;
        }
        
        .code-block .function {
            color: #63b3ed;
        }
        
        .code-block .number {
            color: #f687b3;
        }
        .param-table {
            width: 100%;
            border-collapse: collapse;
            margin: 15px 0;
        }
        .param-table th, .param-table td {
            border: 1px solid #dee2e6;
            padding: 8px 12px;
            text-align: left;
        }
        .param-table th {
            background: #f8f9fa;
            font-weight: bold;
        }
        .required {
            color: #dc3545;
            font-weight: bold;
        }
        .optional {
            color: #6c757d;
        }
        .example {
            background: #e7f3ff;
            border-left: 4px solid #007bff;
            padding: 15px;
            margin: 15px 0;
        }
        .note {
            background: #fff3cd;
            border: 1px solid #ffeaa7;
            border-radius: 5px;
            padding: 15px;
            margin: 15px 0;
        }
        .error {
            background: #f8d7da;
            border: 1px solid #f5c6cb;
            border-radius: 5px;
            padding: 15px;
            margin: 15px 0;
        }
        
        /* 测试区域样式 */
        .test-section {
            background: #f8f9fa;
            border: 1px solid #dee2e6;
            border-radius: 8px;
            padding: 25px;
            margin: 25px 0;
        }
        .test-section h3 {
            color: #495057;
            border-bottom: 2px solid #007bff;
            padding-bottom: 10px;
            margin-top: 0;
        }
        .test-form {
            margin-top: 20px;
        }
        .form-row {
            display: flex;
            gap: 15px;
            margin-bottom: 15px;
        }
        .form-group {
            flex: 1;
        }
        .form-group label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
            color: #495057;
        }
        .form-group textarea {
            width: 100%;
            height: 80px;
            padding: 10px;
            border: 1px solid #ced4da;
            border-radius: 4px;
            font-family: inherit;
            resize: vertical;
        }
        .form-group select {
            width: 100%;
            padding: 8px;
            border: 1px solid #ced4da;
            border-radius: 4px;
            font-size: 14px;
        }
        .test-btn {
            background: #007bff;
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 5px;
            cursor: pointer;
            font-size: 14px;
            margin-right: 10px;
        }
        .test-btn:hover {
            background: #0056b3;
        }
        .test-btn:disabled {
            background: #ccc;
            cursor: not-allowed;
        }
        .stop-btn {
            background: #dc3545;
        }
        .stop-btn:hover {
            background: #c82333;
        }
        .test-result {
            margin-top: 15px;
            padding: 15px;
            background: #f8f9fa;
            border-radius: 5px;
            border-left: 4px solid #007bff;
            white-space: pre-wrap;
            font-family: 'Courier New', monospace;
            font-size: 14px;
            min-height: 20px;
        }
        .test-result.success {
            border-left-color: #28a745;
            background: #d4edda;
            color: #155724;
        }
        .test-result.error {
            border-left-color: #dc3545;
            background: #f8d7da;
            color: #721c24;
        }
        .test-result.loading {
            border-left-color: #ffc107;
            background: #fff3cd;
            color: #856404;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>🌐 SocBackend API 文档</h1>
        
        <div class="note">
            <strong>服务器信息:</strong><br>
            - 基础URL: <code>http://localhost:3001</code><br>
            - 协议: HTTP/HTTPS<br>
            - 内容类型: application/json
        </div>

        <h2>📋 概述</h2>
        <p>这是一个基于Rust和Axum框架构建的后端服务，提供以下主要功能：</p>
        <ul>
            <li><strong>文本翻译</strong> - 基于腾讯云翻译API的多语言翻译服务</li>
            <li><strong>语音合成</strong> - 基于讯飞开放平台的文本转语音服务</li>
            <li><strong>实时语音合成</strong> - 基于WebSocket的流式语音合成</li>
        </ul>

        <h2>🔧 环境配置</h2>
        <p>服务需要以下环境变量：</p>
        <div class="code-block">
TENCENT_SECRET_ID=你的腾讯云SecretId
TENCENT_SECRET_KEY=你的腾讯云SecretKey
XFYUN_APP_ID=你的讯飞AppId
XFYUN_API_KEY=你的讯飞ApiKey
XFYUN_API_SECRET=你的讯飞ApiSecret
PORT=3001  # 可选，默认为3001
        </div>

        <h2>📡 API 接口</h2>

        <h3>1. 文本翻译</h3>
        <div class="endpoint">
            <span class="method">POST</span>
            <span class="url">/translate</span>
        </div>
        
        <p>将文本从一种语言翻译为另一种语言。</p>

        <h4>请求参数</h4>
        <table class="param-table">
            <tr>
                <th>参数名</th>
                <th>类型</th>
                <th>必需</th>
                <th>默认值</th>
                <th>描述</th>
            </tr>
            <tr>
                <td>text</td>
                <td>string</td>
                <td class="required">是</td>
                <td>-</td>
                <td>要翻译的文本内容</td>
            </tr>
            <tr>
                <td>source_lang</td>
                <td>string</td>
                <td class="optional">否</td>
                <td>"auto"</td>
                <td>源语言代码，支持"auto"自动检测</td>
            </tr>
            <tr>
                <td>target_lang</td>
                <td>string</td>
                <td class="optional">否</td>
                <td>"zh"</td>
                <td>目标语言代码</td>
            </tr>
        </table>

        <h4>支持的语言代码</h4>
        <div class="code-block">
auto - 自动检测
zh - 中文
en - 英语
ja - 日语
ko - 韩语
fr - 法语
es - 西班牙语
it - 意大利语
de - 德语
tr - 土耳其语
ru - 俄语
pt - 葡萄牙语
vi - 越南语
id - 印尼语
th - 泰语
ms - 马来语
        </div>

        <h4>请求示例</h4>
        <div class="example">
            <strong>JavaScript 示例:</strong><br>
            <div class="code-block">
// 翻译文本函数
async function translateText() {
    try {
        // 发送翻译请求
        const response = await fetch('http://localhost:3001/translate', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                text: 'Hello, how are you today?',
                source_lang: 'en',
                target_lang: 'zh'
            })
        });

        // 检查响应状态
        if (!response.ok) {
            throw new Error(`HTTP ${response.status}: ${response.statusText}`);
        }

        // 解析响应数据
        const data = await response.json();
        console.log('翻译结果:', data.translated_text);
        
        return data.translated_text;
    } catch (error) {
        console.error('翻译失败:', error);
        throw error;
    }
}

// 调用示例
translateText()
    .then(result => {
        console.log('翻译成功:', result);
    })
    .catch(error => {
        console.error('翻译失败:', error);
    });
            </div>
        </div>

        <h4>响应格式</h4>
        <div class="code-block">
{
  "translated_text": "你好，你今天怎么样？"
}
        </div>

        <h3>2. 语音合成 (HTTP)</h3>
        <div class="endpoint">
            <span class="method">POST</span>
            <span class="url">/synthesize</span>
        </div>
        
        <p>将文本转换为语音，返回base64编码的WAV音频数据。</p>

        <h4>请求参数</h4>
        <table class="param-table">
            <tr>
                <th>参数名</th>
                <th>类型</th>
                <th>必需</th>
                <th>描述</th>
            </tr>
            <tr>
                <td>text</td>
                <td>string</td>
                <td class="required">是</td>
                <td>要转换为语音的文本内容</td>
            </tr>
        </table>

        <h4>请求示例</h4>
        <div class="example">
            <strong>JavaScript 示例:</strong><br>
            <div class="code-block">
// 语音合成函数
async function synthesizeSpeech() {
    try {
        // 发送语音合成请求
        const response = await fetch('http://localhost:3001/synthesize', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                text: '你好，这是一个语音合成测试。'
            })
        });

        // 检查响应状态
        if (!response.ok) {
            throw new Error(`HTTP ${response.status}: ${response.statusText}`);
        }

        // 解析响应数据
        const data = await response.json();
        console.log('合成成功:', data.message);
        
        // 将base64音频数据转换为Blob
        const audioBlob = new Blob([
            Uint8Array.from(atob(data.audio_data), c => c.charCodeAt(0))
        ], { type: data.audio_format });
        
        // 创建音频URL并播放
        const audioUrl = URL.createObjectURL(audioBlob);
        const audio = new Audio(audioUrl);
        audio.play();
        
        return data;
    } catch (error) {
        console.error('语音合成失败:', error);
        throw error;
    }
}

// 调用示例
synthesizeSpeech()
    .then(result => {
        console.log('语音合成成功:', result);
    })
    .catch(error => {
        console.error('语音合成失败:', error);
    });
            </div>
        </div>

        <h4>响应格式</h4>
        <div class="code-block">
{
  "message": "语音合成成功",
  "audio_data": "UklGRnoGAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQoGAACBhYqFbF1fdJivrJBhNjVgodDbq2EcBj+a2/LDciUFLIHO8tiJNwgZaLvt559NEAxQp+PwtmMcBjiR1/LMeSwFJHfH8N2QQAoUXrTp66hVFApGn+DyvmwhBSuBzvLZiTYIG2m98OScTgwOUarm7blmGgU7k9n1unEiBC13yO/eizEIHWq+8+OWT...",
  "audio_format": "audio/wav"
}
        </div>

        <h3>3. 实时语音合成 (WebSocket)</h3>
        <div class="endpoint">
            <span class="method">WebSocket</span>
            <span class="url">/ws_tts</span>
        </div>
        
        <p>通过WebSocket连接进行实时语音合成，支持流式音频数据传输。</p>

        <h4>连接方式</h4>
        <div class="code-block">
WebSocket URL: ws://localhost:3001/ws_tts
        </div>

        <h4>使用流程</h4>
        <ol>
            <li>建立WebSocket连接到 <code>/ws_tts</code></li>
            <li>发送要合成的文本（文本格式）</li>
            <li>接收二进制PCM音频帧数据</li>
            <li>连接自动关闭</li>
        </ol>

        <h4>JavaScript 示例</h4>
        <div class="example">
            <div class="code-block">
// WebSocket实时语音合成类
class WebSocketTTS {
    constructor() {
        this.ws = null;
        this.audioChunks = [];
        this.isConnected = false;
    }

    // 连接并开始合成
    async synthesize(text) {
        try {
            // 创建WebSocket连接
            this.ws = new WebSocket('ws://localhost:3001/ws_tts');
            this.audioChunks = [];
            
            // 连接建立时的处理
            this.ws.onopen = () => {
                console.log('WebSocket连接已建立');
                this.isConnected = true;
                this.ws.send(text);
            };

            // 接收消息的处理
            this.ws.onmessage = (event) => {
                if (event.data instanceof Blob) {
                    // 接收音频数据块
                    this.audioChunks.push(event.data);
                    console.log(`已接收 ${this.audioChunks.length} 个音频数据块`);
                } else {
                    // 处理文本消息（错误信息）
                    console.log('收到消息:', event.data);
                }
            };

            // 连接关闭时的处理
            this.ws.onclose = () => {
                console.log('WebSocket连接已关闭');
                this.isConnected = false;
                this.playAudio();
            };

            // 连接错误时的处理
            this.ws.onerror = (error) => {
                console.error('WebSocket错误:', error);
                this.isConnected = false;
            };
        } catch (error) {
            console.error('WebSocket连接失败:', error);
            throw error;
        }
    }

    // 播放合并的音频
    playAudio() {
        if (this.audioChunks.length > 0) {
            // 合并所有音频数据块
            const audioBlob = new Blob(this.audioChunks, { type: 'audio/wav' });
            const audioUrl = URL.createObjectURL(audioBlob);
            const audio = new Audio(audioUrl);
            
            // 开始播放
            audio.play();
            console.log('开始播放音频');
        } else {
            console.log('没有接收到音频数据');
        }
    }

    // 停止连接
    stop() {
        if (this.ws && this.isConnected) {
            this.ws.close();
        }
    }
}

// 使用示例
const tts = new WebSocketTTS();

// 开始实时语音合成
tts.synthesize('你好，这是WebSocket实时语音合成测试。');

// 停止合成（可选）
// tts.stop();
            </div>
        </div>

        <h2>⚠️ 错误处理</h2>
        
        <h3>HTTP 状态码</h3>
        <table class="param-table">
            <tr>
                <th>状态码</th>
                <th>描述</th>
            </tr>
            <tr>
                <td>200</td>
                <td>请求成功</td>
            </tr>
            <tr>
                <td>400</td>
                <td>请求参数错误</td>
            </tr>
            <tr>
                <td>500</td>
                <td>服务器内部错误</td>
            </tr>
        </table>

        <h3>常见错误</h3>
        <div class="error">
            <strong>翻译服务错误:</strong><br>
            - 腾讯云API密钥配置错误<br>
            - 网络连接问题<br>
            - 不支持的语言代码
        </div>

        <div class="error">
            <strong>语音合成错误:</strong><br>
            - 讯飞API密钥配置错误<br>
            - 文本内容过长<br>
            - WebSocket连接异常
        </div>

        <h2>🔒 安全说明</h2>
        <div class="note">
            <ul>
                <li>所有API密钥应通过环境变量配置，不要硬编码在代码中</li>
                <li>生产环境建议使用HTTPS协议</li>
                <li>建议配置适当的CORS策略</li>
                <li>定期轮换API密钥</li>
            </ul>
        </div>

        <h2>🧪 在线测试</h2>
        
        <div class="test-section">
            <h3>1. 翻译功能测试</h3>
            <div class="test-form">
                <div class="form-row">
                    <div class="form-group">
                        <label for="testSourceLang">源语言:</label>
                        <select id="testSourceLang">
                            <option value="auto">自动检测</option>
                            <option value="zh">中文</option>
                            <option value="en" selected>英语</option>
                            <option value="ja">日语</option>
                            <option value="ko">韩语</option>
                            <option value="fr">法语</option>
                            <option value="es">西班牙语</option>
                            <option value="it">意大利语</option>
                            <option value="de">德语</option>
                            <option value="tr">土耳其语</option>
                            <option value="ru">俄语</option>
                            <option value="pt">葡萄牙语</option>
                            <option value="vi">越南语</option>
                            <option value="id">印尼语</option>
                            <option value="th">泰语</option>
                            <option value="ms">马来语</option>
                        </select>
                    </div>
                    <div class="form-group">
                        <label for="testTargetLang">目标语言:</label>
                        <select id="testTargetLang">
                            <option value="zh" selected>中文</option>
                            <option value="en">英语</option>
                            <option value="ja">日语</option>
                            <option value="ko">韩语</option>
                            <option value="fr">法语</option>
                            <option value="es">西班牙语</option>
                            <option value="it">意大利语</option>
                            <option value="de">德语</option>
                            <option value="tr">土耳其语</option>
                            <option value="ru">俄语</option>
                            <option value="pt">葡萄牙语</option>
                            <option value="vi">越南语</option>
                            <option value="id">印尼语</option>
                            <option value="th">泰语</option>
                            <option value="ms">马来语</option>
                        </select>
                    </div>
                </div>
                <div class="form-group">
                    <label for="testTranslateText">要翻译的文本:</label>
                    <textarea id="testTranslateText" placeholder="请输入要翻译的文本...">Hello, how are you today?</textarea>
                </div>
                <button id="testTranslateBtn" class="test-btn">测试翻译</button>
                <div id="testTranslateResult" class="test-result"></div>
            </div>
        </div>

        <div class="test-section">
            <h3>2. 语音合成测试</h3>
            <div class="test-form">
                <div class="form-group">
                    <label for="testSynthesizeText">要合成的文本:</label>
                    <textarea id="testSynthesizeText" placeholder="请输入要转换为语音的文本...">你好，这是一个语音合成测试。</textarea>
                </div>
                <button id="testSynthesizeBtn" class="test-btn">测试语音合成</button>
                <div id="testSynthesizeResult" class="test-result"></div>
                <div id="audioPlayer" style="display: none; margin-top: 15px;">
                    <audio id="testAudio" controls style="width: 100%;">
                        您的浏览器不支持音频播放。
                    </audio>
                </div>
            </div>
        </div>

        <div class="test-section">
            <h3>3. WebSocket实时语音合成测试</h3>
            <div class="test-form">
                <div class="form-group">
                    <label for="testWsText">要合成的文本:</label>
                    <textarea id="testWsText" placeholder="请输入要转换为语音的文本...">这是WebSocket实时语音合成测试。</textarea>
                </div>
                <button id="testWsBtn" class="test-btn">开始实时合成</button>
                <button id="stopWsBtn" class="test-btn stop-btn" style="display: none;">停止合成</button>
                <div id="testWsResult" class="test-result"></div>
                <div id="wsAudioPlayer" style="display: none; margin-top: 15px;">
                    <audio id="wsAudio" controls style="width: 100%;">
                        您的浏览器不支持音频播放。
                    </audio>
                </div>
            </div>
        </div>

        <h2>📝 使用示例</h2>
        
        <h3>完整的前端集成示例</h3>
        <div class="example">
            <div class="code-block">
// ========================================
// API客户端类 - 封装所有后端接口调用
// ========================================
class SocBackendClient {
    constructor(baseUrl = 'http://localhost:3001') {
        this.baseUrl = baseUrl;
    }

    // ========================================
    // 翻译文本功能
    // ========================================
    async translateText(text, sourceLang = 'auto', targetLang = 'zh') {
        try {
            // 发送翻译请求
            const response = await fetch(`${this.baseUrl}/translate`, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({
                    text: text,
                    source_lang: sourceLang,
                    target_lang: targetLang
                })
            });

            // 检查响应状态
            if (!response.ok) {
                throw new Error(`HTTP ${response.status}: ${response.statusText}`);
            }

            // 解析并返回结果
            const data = await response.json();
            return data.translated_text;
        } catch (error) {
            console.error('翻译失败:', error);
            throw error;
        }
    }

    // ========================================
    // 语音合成功能
    // ========================================
    async synthesizeSpeech(text) {
        try {
            // 发送语音合成请求
            const response = await fetch(`${this.baseUrl}/synthesize`, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({
                    text: text
                })
            });

            // 检查响应状态
            if (!response.ok) {
                throw new Error(`HTTP ${response.status}: ${response.statusText}`);
            }

            // 解析响应数据
            const data = await response.json();
            
            // 将base64音频数据转换为Blob
            const audioBlob = new Blob([
                Uint8Array.from(atob(data.audio_data), c => c.charCodeAt(0))
            ], { type: data.audio_format });
            
            // 创建音频对象
            const audioUrl = URL.createObjectURL(audioBlob);
            const audio = new Audio(audioUrl);
            
            // 返回音频播放器对象
            return {
                audio: audio,
                data: data,
                play: () => audio.play()
            };
        } catch (error) {
            console.error('语音合成失败:', error);
            throw error;
        }
    }

    // ========================================
    // WebSocket实时语音合成
    // ========================================
    createWebSocketTTS() {
        return new WebSocketTTS(this.baseUrl);
    }
}

// ========================================
// WebSocket TTS类 - 处理实时语音合成
// ========================================
class WebSocketTTS {
    constructor(baseUrl) {
        this.baseUrl = baseUrl;
        this.ws = null;
        this.audioChunks = [];
        this.isConnected = false;
        
        // 回调函数
        this.onProgress = null;
        this.onComplete = null;
        this.onError = null;
    }

    // 开始实时语音合成
    async synthesize(text, callbacks = {}) {
        // 设置回调函数
        this.onProgress = callbacks.onProgress;
        this.onComplete = callbacks.onComplete;
        this.onError = callbacks.onError;

        try {
            // 创建WebSocket连接
            const wsUrl = `ws://${this.baseUrl.replace('http://', '')}/ws_tts`;
            this.ws = new WebSocket(wsUrl);
            this.audioChunks = [];
            
            // 连接建立时的处理
            this.ws.onopen = () => {
                console.log('WebSocket连接已建立');
                this.isConnected = true;
                this.ws.send(text);
            };

            // 接收消息的处理
            this.ws.onmessage = (event) => {
                if (event.data instanceof Blob) {
                    // 接收音频数据块
                    this.audioChunks.push(event.data);
                    
                    // 触发进度回调
                    if (this.onProgress) {
                        this.onProgress(this.audioChunks.length);
                    }
                } else {
                    // 处理文本消息（错误信息）
                    console.log('收到消息:', event.data);
                }
            };

            // 连接关闭时的处理
            this.ws.onclose = () => {
                console.log('WebSocket连接已关闭');
                this.isConnected = false;
                this.playAudio();
            };

            // 连接错误时的处理
            this.ws.onerror = (error) => {
                console.error('WebSocket错误:', error);
                this.isConnected = false;
                
                // 触发错误回调
                if (this.onError) {
                    this.onError(error);
                }
            };
        } catch (error) {
            console.error('WebSocket连接失败:', error);
            
            // 触发错误回调
            if (this.onError) {
                this.onError(error);
            }
            throw error;
        }
    }

    // 播放合并的音频
    playAudio() {
        if (this.audioChunks.length > 0) {
            // 合并所有音频数据块
            const audioBlob = new Blob(this.audioChunks, { type: 'audio/wav' });
            const audioUrl = URL.createObjectURL(audioBlob);
            const audio = new Audio(audioUrl);
            
            // 开始播放
            audio.play();
            
            // 触发完成回调
            if (this.onComplete) {
                this.onComplete(audio, this.audioChunks.length);
            }
        }
    }

    // 停止连接
    stop() {
        if (this.ws && this.isConnected) {
            this.ws.close();
        }
    }
}

// ========================================
// 使用示例
// ========================================

// 创建客户端实例
const client = new SocBackendClient();

// 翻译功能示例
async function translateExample() {
    try {
        const result = await client.translateText('Hello, world!', 'en', 'zh');
        console.log('翻译结果:', result);
    } catch (error) {
        console.error('翻译失败:', error);
    }
}

// 语音合成示例
async function synthesizeExample() {
    try {
        const audioPlayer = await client.synthesizeSpeech('你好，世界！');
        audioPlayer.play();
    } catch (error) {
        console.error('语音合成失败:', error);
    }
}

// WebSocket实时语音合成示例
function wsTTSExample() {
    const wsTTS = client.createWebSocketTTS();
    
    wsTTS.synthesize('这是实时语音合成测试。', {
        // 进度回调
        onProgress: (chunkCount) => {
            console.log(`已接收 ${chunkCount} 个音频块`);
        },
        
        // 完成回调
        onComplete: (audio, totalChunks) => {
            console.log(`合成完成，共 ${totalChunks} 个音频块`);
        },
        
        // 错误回调
        onError: (error) => {
            console.error('合成错误:', error);
        }
    });
}

// 执行示例
translateExample();
synthesizeExample();
wsTTSExample();
            </div>
        </div>

        <h2>📞 技术支持</h2>
        <p>如有问题，请检查：</p>
        <ul>
            <li>环境变量是否正确配置</li>
            <li>网络连接是否正常</li>
            <li>API密钥是否有效</li>
            <li>服务器日志输出</li>
        </ul>

        <hr>
        <p style="text-align: center; color: #666; margin-top: 30px;">
            SocBackend API 文档 | 版本 1.0 | 基于 Rust + Axum 构建
        </p>
    </div>

    <script>
        const API_BASE = 'http://localhost:3001';
        let wsConnection = null;
        let wsAudioChunks = [];

        // 等待DOM加载完成后绑定事件
        document.addEventListener('DOMContentLoaded', function() {
            // 绑定翻译测试按钮
            document.getElementById('testTranslateBtn').addEventListener('click', testTranslate);
            
            // 绑定语音合成测试按钮
            document.getElementById('testSynthesizeBtn').addEventListener('click', testSynthesize);
            
            // 绑定WebSocket测试按钮
            document.getElementById('testWsBtn').addEventListener('click', testWebSocket);
            document.getElementById('stopWsBtn').addEventListener('click', stopWebSocket);
            
            // 回车键快捷操作
            document.getElementById('testTranslateText').addEventListener('keypress', function(e) {
                if (e.key === 'Enter' && e.ctrlKey) {
                    testTranslate();
                }
            });
            
            document.getElementById('testSynthesizeText').addEventListener('keypress', function(e) {
                if (e.key === 'Enter' && e.ctrlKey) {
                    testSynthesize();
                }
            });
            
            document.getElementById('testWsText').addEventListener('keypress', function(e) {
                if (e.key === 'Enter' && e.ctrlKey) {
                    testWebSocket();
                }
            });
        });

        // 翻译测试函数
        async function testTranslate() {
            // 获取用户输入
            const sourceLang = document.getElementById('testSourceLang').value;
            const targetLang = document.getElementById('testTargetLang').value;
            const text = document.getElementById('testTranslateText').value.trim();
            const resultDiv = document.getElementById('testTranslateResult');
            const btn = document.getElementById('testTranslateBtn');
            
            // 验证输入
            if (!text) {
                showResult(resultDiv, '请输入要翻译的文本', 'error');
                return;
            }
            
            // 显示加载状态
            btn.disabled = true;
            btn.textContent = '翻译中...';
            showResult(resultDiv, '正在翻译...', 'loading');
            
            try {
                // 发送翻译请求
                const response = await fetch(`${API_BASE}/translate`, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({
                        text: text,
                        source_lang: sourceLang,
                        target_lang: targetLang
                    })
                });
                
                // 检查响应状态
                if (!response.ok) {
                    throw new Error(`HTTP ${response.status}: ${response.statusText}`);
                }
                
                // 解析响应数据
                const data = await response.json();
                showResult(resultDiv, `翻译成功！\n\n原文: ${text}\n译文: ${data.translated_text}`, 'success');
                
            } catch (error) {
                console.error('翻译失败:', error);
                showResult(resultDiv, `翻译失败: ${error.message}`, 'error');
            } finally {
                // 恢复按钮状态
                btn.disabled = false;
                btn.textContent = '测试翻译';
            }
        }

        // 语音合成测试函数
        async function testSynthesize() {
            // 获取用户输入
            const text = document.getElementById('testSynthesizeText').value.trim();
            const resultDiv = document.getElementById('testSynthesizeResult');
            const btn = document.getElementById('testSynthesizeBtn');
            const audioPlayer = document.getElementById('audioPlayer');
            const audio = document.getElementById('testAudio');
            
            // 验证输入
            if (!text) {
                showResult(resultDiv, '请输入要合成的文本', 'error');
                return;
            }
            
            // 显示加载状态
            btn.disabled = true;
            btn.textContent = '合成中...';
            showResult(resultDiv, '正在合成语音...', 'loading');
            audioPlayer.style.display = 'none';
            
            try {
                // 发送语音合成请求
                const response = await fetch(`${API_BASE}/synthesize`, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({
                        text: text
                    })
                });
                
                // 检查响应状态
                if (!response.ok) {
                    throw new Error(`HTTP ${response.status}: ${response.statusText}`);
                }
                
                // 解析响应数据
                const data = await response.json();
                
                // 将base64音频数据转换为Blob
                const audioBlob = new Blob([
                    Uint8Array.from(atob(data.audio_data), c => c.charCodeAt(0))
                ], { type: data.audio_format });
                
                // 创建音频URL并设置播放器
                const audioUrl = URL.createObjectURL(audioBlob);
                audio.src = audioUrl;
                audioPlayer.style.display = 'block';
                
                // 显示成功结果
                showResult(resultDiv, `语音合成成功！\n\n文本: ${text}\n音频格式: ${data.audio_format}\n数据长度: ${data.audio_data.length} 字符`, 'success');
                
                // 自动播放音频
                audio.play().catch(e => {
                    console.log('自动播放失败:', e);
                });
                
            } catch (error) {
                console.error('语音合成失败:', error);
                showResult(resultDiv, `语音合成失败: ${error.message}`, 'error');
            } finally {
                // 恢复按钮状态
                btn.disabled = false;
                btn.textContent = '测试语音合成';
            }
        }

        // WebSocket实时语音合成测试函数
        async function testWebSocket() {
            // 获取用户输入
            const text = document.getElementById('testWsText').value.trim();
            const resultDiv = document.getElementById('testWsResult');
            const startBtn = document.getElementById('testWsBtn');
            const stopBtn = document.getElementById('stopWsBtn');
            const audioPlayer = document.getElementById('wsAudioPlayer');
            const audio = document.getElementById('wsAudio');
            
            // 验证输入
            if (!text) {
                showResult(resultDiv, '请输入要合成的文本', 'error');
                return;
            }
            
            // 显示加载状态
            startBtn.style.display = 'none';
            stopBtn.style.display = 'inline-block';
            showResult(resultDiv, '正在连接WebSocket...', 'loading');
            audioPlayer.style.display = 'none';
            wsAudioChunks = [];
            
            try {
                // 创建WebSocket连接
                wsConnection = new WebSocket(`ws://localhost:3001/ws_tts`);
                
                // 连接建立时的处理
                wsConnection.onopen = function() {
                    showResult(resultDiv, 'WebSocket连接已建立，正在发送文本...', 'loading');
                    wsConnection.send(text);
                };
                
                // 接收消息的处理
                wsConnection.onmessage = function(event) {
                    if (event.data instanceof Blob) {
                        // 接收音频数据块
                        wsAudioChunks.push(event.data);
                        showResult(resultDiv, `正在接收音频数据... (已接收 ${wsAudioChunks.length} 个数据块)`, 'loading');
                    } else {
                        // 接收文本消息（错误信息）
                        showResult(resultDiv, `收到消息: ${event.data}`, 'error');
                    }
                };
                
                // 连接关闭时的处理
                wsConnection.onclose = function() {
                    showResult(resultDiv, 'WebSocket连接已关闭，正在处理音频数据...', 'loading');
                    
                    // 合并所有音频数据
                    if (wsAudioChunks.length > 0) {
                        const audioBlob = new Blob(wsAudioChunks, { type: 'audio/wav' });
                        const audioUrl = URL.createObjectURL(audioBlob);
                        audio.src = audioUrl;
                        audioPlayer.style.display = 'block';
                        
                        showResult(resultDiv, `实时语音合成完成！\n\n文本: ${text}\n接收数据块: ${wsAudioChunks.length} 个`, 'success');
                        
                        // 自动播放音频
                        audio.play().catch(e => {
                            console.log('自动播放失败:', e);
                        });
                    } else {
                        showResult(resultDiv, '未接收到音频数据', 'error');
                    }
                    
                    // 恢复按钮状态
                    startBtn.style.display = 'inline-block';
                    stopBtn.style.display = 'none';
                };
                
                // 连接错误时的处理
                wsConnection.onerror = function(error) {
                    showResult(resultDiv, `WebSocket连接错误: ${error}`, 'error');
                    startBtn.style.display = 'inline-block';
                    stopBtn.style.display = 'none';
                };
                
            } catch (error) {
                console.error('WebSocket连接失败:', error);
                showResult(resultDiv, `WebSocket连接失败: ${error.message}`, 'error');
                startBtn.style.display = 'inline-block';
                stopBtn.style.display = 'none';
            }
        }

        // 停止WebSocket连接
        function stopWebSocket() {
            if (wsConnection) {
                wsConnection.close();
                wsConnection = null;
            }
            
            const startBtn = document.getElementById('testWsBtn');
            const stopBtn = document.getElementById('stopWsBtn');
            startBtn.style.display = 'inline-block';
            stopBtn.style.display = 'none';
            
            showResult(document.getElementById('testWsResult'), '已停止WebSocket连接', 'error');
        }

        // 显示结果的辅助函数
        function showResult(element, message, type) {
            element.textContent = message;
            element.className = `test-result ${type}`;
        }

        // 简单的JavaScript语法高亮
        function highlightJavaScript(code) {
            return code
                .replace(/(\/\/.*$)/gm, '<span class="comment">$1</span>')
                .replace(/\b(async|await|function|class|const|let|var|if|else|try|catch|throw|return|new|this|constructor)\b/g, '<span class="keyword">$1</span>')
                .replace(/\b(console|fetch|JSON|URL|Audio|Blob|WebSocket)\b/g, '<span class="function">$1</span>')
                .replace(/(['"`])((?:\\.|(?!\1)[^\\])*?)\1/g, '<span class="string">$1$2$1</span>')
                .replace(/\b(\d+)\b/g, '<span class="number">$1</span>');
        }

        // 应用语法高亮到所有代码块
        function applySyntaxHighlighting() {
            const codeBlocks = document.querySelectorAll('.code-block');
            codeBlocks.forEach(block => {
                if (block.textContent.includes('function') || block.textContent.includes('const') || block.textContent.includes('async')) {
                    block.innerHTML = highlightJavaScript(block.textContent);
                }
            });
        }

        // 获取语言名称的辅助函数
        function getLanguageName(code) {
            const languages = {
                'auto': '自动检测',
                'zh': '中文',
                'en': '英语',
                'ja': '日语',
                'ko': '韩语',
                'fr': '法语',
                'es': '西班牙语',
                'it': '意大利语',
                'de': '德语',
                'tr': '土耳其语',
                'ru': '俄语',
                'pt': '葡萄牙语',
                'vi': '越南语',
                'id': '印尼语',
                'th': '泰语',
                'ms': '马来语'
            };
            return languages[code] || code;
        }

        // 页面加载完成后应用语法高亮
        document.addEventListener('DOMContentLoaded', function() {
            applySyntaxHighlighting();
        });
    </script>
</body>
</html> 